package com.game.framework.balance;

import com.game.framework.balance.model.ServerInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 网关后面的业务服务管理
 */
@Slf4j
public class BusinessServerService implements ApplicationListener<HeartbeatEvent> {

	@Autowired
	protected DiscoveryClient discoveryClient;

	// serviceId对应的服务器集合，一个服务可能部署到多台服务器上面，实现负载均衡
	protected Map<Integer, List<ServerInfo>> serverInfoMap;

	@PostConstruct
	public void init() {
		log.info("初始化, {}, this.discoveryClient = {}", this.getClass().getName(), this.discoveryClient.getServices().toString());
		this.refreshBusinessServerInfo();
	}

	public Set<Integer> getAllServiceId() {
		return this.serverInfoMap.keySet();
	}
	// 刷新网关后面的服务列表
	private void refreshBusinessServerInfo() {
		Map<Integer, List<ServerInfo>> tempServerInfoMap = new HashMap<>();
		List<String> services = this.discoveryClient.getServices();
		for (String serviceId : services) {
			List<ServiceInstance> businessServiceInstances = this.discoveryClient.getInstances(serviceId);// 网取网关后面的服务实例
			businessServiceInstances.forEach(instance -> {
				int weight = this.getServerInfoWeight(instance);
				for (int i = 0; i < weight; i++) {
					ServerInfo serverInfo = this.newServerInfo(instance);
					if (serverInfo != null) {
						List<ServerInfo> serverList = tempServerInfoMap.get(serverInfo.getServiceId());
						if (CollectionUtils.isEmpty(serverList)) {
							serverList = new ArrayList<>();
							tempServerInfoMap.put(serverInfo.getServiceId(), serverList);
						}
						serverList.add(serverInfo);
						log.info("加载实例{}:{}成功！", instance.getHost(), instance.getPort());
					}
				}
			});
		}
		this.serverInfoMap = tempServerInfoMap;
	}
	// 从游戏网关列表中选择一个游戏服务实例信息返回。
	public ServerInfo selectServerInfo(Integer serviceId, Long userId) {
		// 再次声明一下，防止游戏网关列表发生变化，导致数据不一致。
		Map<Integer, List<ServerInfo>> serverInfoMap = this.serverInfoMap;
		List<ServerInfo> serverList = serverInfoMap.get(serviceId);
		if (CollectionUtils.isEmpty(serverList)) {
			return null;
		}
		int hashCode = Math.abs(userId.hashCode());
		int gatewayCount = serverList.size();
		int index = hashCode % gatewayCount;
		if (index >= gatewayCount) {
			index = gatewayCount - 1;
		}
		return serverList.get(index);
	}

	/**
	 * 判断某个服务中的serverId是否还有效
	 */
	public boolean isEnableServer(Integer serviceId, Integer serverId) {
		Map<Integer, List<ServerInfo>> serverInfoMap = this.serverInfoMap;
		List<ServerInfo> serverInfoList = serverInfoMap.get(serviceId);
		if (serverInfoList != null) {
			return serverInfoList.stream().anyMatch(c -> c.getServerId() == serverId);
		}
		return false;

	}

	private ServerInfo newServerInfo(ServiceInstance instance) {
		String serviceId = instance.getMetadata().get("serviceId");
		if (StringUtils.isEmpty(serviceId)) {
			log.info("加载实例{}:{}时，此实例未配置serviceId",
					instance.getHost(), instance.getPort());
			return null;
		}
		String serverId = instance.getMetadata().get("serverId");
		if (StringUtils.isEmpty(serverId)) {
			log.info("加载实例加载实例{}:{}时，此实例未配置serverId",
					instance.getHost(), instance.getPort());
			return null;
		}
		ServerInfo serverInfo = new ServerInfo();
		serverInfo.setServiceId(Integer.parseInt(serviceId));
		serverInfo.setServerId(Integer.parseInt(serverId));
		serverInfo.setHost(instance.getHost());
		serverInfo.setPort(instance.getPort());
		return serverInfo;
	}

	private int getServerInfoWeight(ServiceInstance instance) {
		String value = instance.getMetadata().get("weight");
		if (value == null) {
			value = "1";
		}
		return Integer.parseInt(value);
	}

	@Override
	public void onApplicationEvent(HeartbeatEvent event) {
		this.refreshBusinessServerInfo();
	}
}
